﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.IO;
using System.Threading;

namespace comm.world
{
    #region PathInfo
    public class PathInfo
    {
        public comm.objst.PathType Type { get; private set; }
        public int PathSize { get; private set; }
        public bool MultiObj { get; private set; }
        public int AllObjectCount { get { return _objList.Count; } }

        List<string> _objList = new List<string>();

        public PathInfo(int pathSize, bool multiObj, comm.objst.PathType type)
        {
            PathSize = pathSize;
            MultiObj = multiObj;
            Type = type;
        }

        public bool Empty()
        {
            return Type == comm.objst.PathType.EMPTY && AllObjectCount == 0;
        }
        public bool CanEnter()
        {
            return Type == comm.objst.PathType.EMPTY && (AllObjectCount == 0 || MultiObj);
        }
        public void AddObject(string objectId)
        {
            lock (_objList)
            {
                if (!_objList.Contains(objectId))
                {
                    _objList.Add(objectId);
                }
            }
        }
        public void RemoveObject(string objectId)
        {
            lock (_objList)
            {
                if (_objList.Contains(objectId))
                {
                    _objList.Remove(objectId);
                }
            }
        }
        public void ClearAllObject()
        {
            lock (_objList)
            {
                _objList.Clear();
            }
        }
        public string[] GetAllObject()
        {
            return _objList.ToArray();
        }
    }
    #endregion

    #region BlockInfo
    public class BlockInfo
    {
        public int AllObjectCount { get { return _objList.Count; } }

        List<string> _objList = new List<string>();

        public void AddObject(string objectId)
        {
            lock (_objList)
            {
                if (!_objList.Contains(objectId))
                {
                    _objList.Add(objectId);
                }
            }
        }
        public void RemoveObject(string objectId)
        {
            lock (_objList)
            {
                if (_objList.Contains(objectId))
                {
                    _objList.Remove(objectId);
                }
            }
        }
        public void ClearALlObject()
        {
            lock (_objList)
            {
                _objList.Clear();
            }
        }
        public string[] GetAllObject()
        {
            return _objList.ToArray();
        }
    }
    #endregion

    #region FindTask
    public enum FindTaskMode
    {
        CAN_ENTER = 0,
        EMPTY = 1
    }
    public class FindTask
    {
        private string _id = "";
        public string Id
        {
            get { return _id; }
            set { _id = value; }
        }

        public int SX { get; set; }
        public int SY { get; set; }
        public int EX { get; set; }
        public int EY { get; set; }
        public int PathSize { get; set; }

        public Action<FindTaskMode, comm.AStarNode[]> Callback = null;
    }
    #endregion

    #region Observer
    public class Observer
    {
        public int X { get { return _sobj.LocX; } }
        public int Y { get { return _sobj.LocY; } }
        public int PathSize { get { return _sobj.PathSize; } }
        public int Radius { get { return _sobj.ObservedRadius; } }
        public bool Enabled { get { return _enabled; } set { _enabled = value; } }

        bool _enabled = true;
        BSpace.LocManager _locManager = null;
        BSObject _sobj = null;
        Dictionary<string, ObservedResult> _objDic = new Dictionary<string, ObservedResult>();
        List<ObservedResult> _resultList = new List<ObservedResult>();

        public Observer(BSpace.LocManager locManager, BSObject sobj)
        {
            _locManager = locManager;
            _sobj = sobj;
        }

        void AddObject(BSObject sobj, double distance)
        {
            lock (_objDic)
            {
                if (!_objDic.ContainsKey(sobj.ObjectId))
                {
                    var result = new ObservedResult { BSObject = sobj, Distance = distance };
                    _objDic.Add(sobj.ObjectId, result);
                    _resultList.Add(result);
                    _resultList.SortByDistance();
                    _sobj.ReceiveResultOfObserved(ObservedResultType.ADD, result);
                }
                else
                {
                    if (_objDic[sobj.ObjectId].Distance != distance)
                    {
                        _resultList.SortByDistance();
                        _sobj.ReceiveResultOfObserved(ObservedResultType.RESULT_OBJ_CHANGE, _objDic[sobj.ObjectId]);
                    }
                }
            }
        }
        void RemoveObject(BSObject sobj)
        {
            lock (_objDic)
            {
                if (_objDic.ContainsKey(sobj.ObjectId))
                {
                    var result = _objDic[sobj.ObjectId];
                    _resultList.Remove(result);
                    _objDic.Remove(sobj.ObjectId);
                    _sobj.ReceiveResultOfObserved(ObservedResultType.REMOVE, result);
                }
            }
        }

        public void Observed(string objectId, int x, int y, int pathSize)
        {
            if (Enabled && Radius > 0 && _sobj.ObjectId != objectId)
            {
                var sobj = _sobj.BSpace.GetBSObject(objectId);
                if (sobj != null)
                {
                    var d = _locManager.CheckDistance(X, Y, PathSize, x, y, pathSize, Radius);
                    if (d != -1)
                    {
                        AddObject(sobj, d);
                    }
                    else
                    {
                        RemoveObject(sobj);
                    }
                }
            }
        }
        public ObservedResult[] GetAllResult()
        {
            return _resultList.ToArray();
        }
    }
    public class ObservedResult
    {
        public BSObject BSObject { get; set; }
        public double Distance { get; set; }
    }
    public enum ObservedResultType
    {
        ADD = 0,
        REMOVE = 1,
        RESULT_OBJ_CHANGE = 2
    }
    #endregion

    public abstract class BSpace : BObject
    {
        #region LocManager
        public class LocManager
        {
            public readonly int[] PathSizes = new int[] { 1, 2, 4 };

            public int BlockSize { get; private set; }
            public int BlockWidth { get; private set; }
            public int BlockHeight { get; private set; }
            public int TaskCount { get { return _taskQ.Count; } }
            public int ObserverCount { get { return _observerDic.Count; } }

            Dictionary<int, PathInfo[,]> _pathDataDic = new Dictionary<int, PathInfo[,]>();
            Dictionary<int, int[]> _pathSizeDic = new Dictionary<int, int[]>();
            Dictionary<string, BlockInfo> _blockDic = new Dictionary<string, BlockInfo>();
            Dictionary<string, Observer> _observerDic = new Dictionary<string, Observer>();

            BSpace _space = null;
            bool _selfThread = false;
            Queue<Action> _taskQ = new Queue<Action>();

            public LocManager(BSpace space, comm.objst.DMap.Data dMapData, bool selfThread, int blockSize)
            {
                _space = space;
                _selfThread = selfThread;

                BlockSize = blockSize;
                BlockWidth = (int)Math.Ceiling(_space.MapWidth / (double)BlockSize);
                BlockHeight = (int)Math.Ceiling(_space.MapHeight / (double)BlockSize);

                #region init _pathDataDic
                foreach (var size in PathSizes)
                {
                    int w = _space.MapWidth / size, h = _space.MapHeight / size;
                    _pathSizeDic.Add(size, new int[] { w, h });
                    var infoData = new PathInfo[w, h];
                    for (int x = 0; x < w; x++)
                    {
                        for (int y = 0; y < h; y++)
                        {
                            int px = size * x, py = size * y;
                            var type = comm.objst.PathType.EMPTY;
                            for (int x2 = 0; x2 < size; x2++)
                            {
                                for (int y2 = 0; y2 < size; y2++)
                                {
                                    if (dMapData.GetPathType(px + x2, py + y2) == objst.PathType.FORBID)
                                    {
                                        type = objst.PathType.FORBID;
                                        break;
                                    }
                                }
                                if (type == objst.PathType.FORBID)
                                {
                                    break;
                                }
                            }
                            infoData[x, y] = new PathInfo(size, _space._pathMultiObj, type);
                        }
                    }
                    _pathDataDic.Add(size, infoData);
                }
                #endregion

                #region init _blockDic
                for (int bx = 0; bx < BlockWidth; bx++)
                {
                    for (int by = 0; by < BlockHeight; by++)
                    {
                        _blockDic.Add(bx + "," + by, new BlockInfo());
                    }
                }
                #endregion

                #region task loop
                if (_selfThread)
                {
                    comm.ThreadHelper.Run(() =>
                    {
                        while (true)
                        {
                            if (_taskQ.Count > 0)
                            {
                                _taskQ.Dequeue()();
                            }
                            Thread.Sleep(1);
                        }
                    });
                }
                #endregion
            }

            void AddTask(Action callback)
            {
                if (_selfThread)
                {
                    _taskQ.Enqueue(callback);
                }
                else
                {
                    callback();
                }
            }

            #region block
            BlockInfo GetBlock(int bx, int by)
            {
                return _blockDic[bx + "," + by];
            }
            void BlockRemoveObject(int bx, int by, string objectId)
            {
                if (CheckBlock(bx, by))
                {
                    GetBlock(bx, by).RemoveObject(objectId);
                }
            }
            void BlockAddObject(int bx, int by, string objectId)
            {
                if (CheckBlock(bx, by))
                {
                    GetBlock(bx, by).AddObject(objectId);
                }
            }
            void ClearAllBlockObject()
            {
                foreach (var b in _blockDic.Values)
                {
                    b.ClearALlObject();
                }
            }

            /// <summary>
            /// return [[bx, by, inBlockX, inBlockY],...];
            /// </summary>
            public int[][] LocToBlocks(int x, int y, int pathSize)
            {
                int sx = x * pathSize, sy = y * pathSize;
                var list = new List<int[]>();
                for (int x1 = 0; x1 < pathSize; x1++)
                {
                    for (int y1 = 0; y1 < pathSize; y1++)
                    {
                        int bx = (int)Math.Floor((sx + x1) / (double)BlockSize),
                            by = (int)Math.Floor((sy + y1) / (double)BlockSize);
                        list.Add(new int[] { bx, by, x1 + sx - bx * BlockSize, y1 + sy - by * BlockSize });
                    }
                }
                return list.ToArray();
            }
            public bool CheckBlock(int bx, int by)
            {
                return _blockDic.ContainsKey(bx + "," + by);
            }
            public int GetBlockObjectCount(int bx, int by)
            {
                if (CheckBlock(bx, by))
                {
                    return GetBlock(bx, by).AllObjectCount;
                }
                return 0;
            }
            #endregion

            #region path
            void RemoveObjectInPath(string objectId, int x, int y, int pathSize)
            {
                int sx = x * pathSize, sy = y * pathSize;
                for (int x1 = 0; x1 < pathSize; x1++)
                {
                    for (int y1 = 0; y1 < pathSize; y1++)
                    {
                        foreach (var size in PathSizes)
                        {
                            int px = sx + x1, py = sy + y1;
                            int dx = (int)Math.Floor(px / (double)size), dy = (int)Math.Floor(py / (double)size);
                            GetPathType(dx, dy, size).RemoveObject(objectId);
                        }
                    }
                }
            }
            void AddObjectInPath(string objectId, int x, int y, int pathSize)
            {
                int sx = x * pathSize, sy = y * pathSize;
                for (int x1 = 0; x1 < pathSize; x1++)
                {
                    for (int y1 = 0; y1 < pathSize; y1++)
                    {
                        foreach (var size in PathSizes)
                        {
                            int px = sx + x1, py = sy + y1;
                            int dx = (int)Math.Floor(px / (double)size), dy = (int)Math.Floor(py / (double)size);
                            GetPathType(dx, dy, size).AddObject(objectId);
                        }
                    }
                }
            }
            void ClearAllPathObject()
            {
                foreach (var size in PathSizes)
                {
                    var s = GetMapSize(size);
                    int w = s[0], h = s[1];
                    for (int x = 0; x < w; x++)
                    {
                        for (int y = 0; y < h; y++)
                        {
                            GetPathType(x, y, size).ClearAllObject();
                        }
                    }
                }
            }

            /// <summary>
            /// return [x,y,count]
            /// </summary>
            public int[] GenerateRimLoc(int x, int y, int pathSize)
            {
                if (GetPathType(x, y, pathSize).Empty())
                {
                    return new int[] { x, y, 0 };
                }
                int[] ss = null;
                int maxBor = 10;
                for (int bor = 1; bor <= maxBor; bor++)
                {
                    var dots = comm.MathHelper.Rect(x - bor, y - bor, bor * 2, bor * 2, 1);
                    foreach (var dot in dots)
                    {
                        var info = GetPathType(dot[0], dot[1], pathSize);
                        if (info.Empty())
                        {
                            return new int[] { dot[0], dot[1], 0 };
                        }
                        else if (ss == null && info.Type == comm.objst.PathType.EMPTY)
                        {
                            ss = new int[] { dot[0], dot[1], info.AllObjectCount };
                        }
                    }
                }
                return ss;
            }
            /// <summary>
            /// return new []{ [x,y,count],... }
            /// </summary>
            public int[][] GenerateRandomLoc(int pathSize, uint[] rect = null, uint count = 1)
            {
                uint rx = 0, ry = 0, rw = (uint)(_space.MapWidth / pathSize), rh = (uint)(_space.MapHeight / pathSize);
                if (rect != null && rect.Length == 4)
                {
                    rx = rect[0];
                    ry = rect[1];
                    rw = rect[2];
                    rh = rect[3];
                }
                var qQueue = new Queue<int[]>();
                var pList = new List<int[]>();
                var loop = (rw - rx) * (rh - ry);
                while (true)
                {
                    if (loop <= 0)
                    {
                        var c = count - pList.Count;
                        if (c > 0)
                        {
                            for (int i = 0; i < c; i++)
                            {
                                if (qQueue.Count > 0)
                                {
                                    pList.Add(qQueue.Dequeue());
                                }
                            }
                        }
                        return pList.ToArray();
                    }
                    loop--;
                    var x = comm.MathHelper.RandomInt((int)rx, (int)rw);
                    var y = comm.MathHelper.RandomInt((int)ry, (int)rh);
                    var info = GetPathType(x, y, pathSize);
                    if (info.Empty())
                    {
                        pList.Add(new int[] { x, y, 0 });
                    }
                    else if (qQueue.Count < count && info.Type == comm.objst.PathType.EMPTY)
                    {
                        qQueue.Enqueue(new int[] { x, y, info.AllObjectCount });
                    }
                    if (pList.Count == count)
                    {
                        return pList.ToArray();
                    }
                }
            }
            public PathInfo GetPathType(int x, int y, int pathSize)
            {
                if (!CheckLoc(x, y, pathSize) || !_pathDataDic.ContainsKey(pathSize))
                {
                    return new PathInfo(0, _space._pathMultiObj, comm.objst.PathType.UNDEFINED);
                }
                return _pathDataDic[pathSize][x, y];
            }
            public bool CheckPathCanEnter(int x, int y, int pathSize)
            {
                return GetPathType(x, y, pathSize).CanEnter();
            }
            public bool CheckPathEmpty(int x, int y, int pathSize)
            {
                return GetPathType(x, y, pathSize).Type == comm.objst.PathType.EMPTY;
            }
            public bool CheckLoc(int x, int y, int pathSize)
            {
                return new Rectangle(0, 0, _space.MapWidth / pathSize, _space.MapHeight / pathSize).Contains(x, y);
            }
            public int[] GetMapSize(int pathSize)
            {
                return (int[])_pathSizeDic[pathSize].Clone();
            }
            #endregion

            public double CheckDistance(int fromX, int fromY, int fromPathSize, int toX, int toY, int toPathSize, int maxDistance)
            {
                var ds = new List<double>();
                int count = 0, fail = 0;
                for (int x1 = 0; x1 < fromPathSize; x1++)
                {
                    for (int y1 = 0; y1 < fromPathSize; y1++)
                    {
                        for (int x2 = 0; x2 < toPathSize; x2++)
                        {
                            for (int y2 = 0; y2 < toPathSize; y2++)
                            {
                                count++;
                                var d = comm.MathHelper.PointDistance(
                                    x1 + fromX * fromPathSize,
                                    y1 + fromY * fromPathSize,
                                    x2 + toX * toPathSize,
                                    y2 + toY * toPathSize);
                                if (d <= maxDistance)
                                {
                                    ds.Add(d);
                                }
                                else
                                {
                                    fail++;
                                }
                            }
                        }
                    }
                }
                if (count != fail)
                {
                    ds.Sort();
                    return ds[0];
                }
                return -1;
            }
            public int[][] GenerateLineDots(int fromX, int fromY, int toX, int toY, int stroke)
            {
                stroke = Math.Max(0, stroke - 1);
                if (stroke == 0)
                {
                    return comm.MathHelper.Line(fromX, fromY, toX, toY);
                }
                return comm.MathHelper.Line2D(fromX, fromY, toX, toY, stroke);
            }
            public int[][] GenerateArcDots(int x, int y, int angleStart, int angleEnd, int radius)
            {
                if (angleStart < 0)
                {
                    angleStart += 360;
                }
                return comm.MathHelper.Arc(x - radius, y - radius, radius * 2, radius * 2, angleStart, angleEnd);
            }
            public BSObject[] GetObjectInDots(int[][] dots)
            {
                var ids = new List<string>();
                foreach (var dot in dots)
                {
                    var objs = GetPathType(dot[0], dot[1], 1).GetAllObject();
                    foreach (var objectId in objs)
                    {
                        if (!ids.Contains(objectId))
                        {
                            ids.Add(objectId);
                        }
                    }
                }
                var results = new List<BSObject>();
                foreach (var objectId in ids)
                {
                    var sobj = _space.GetBSObject(objectId);
                    if (sobj != null)
                    {
                        results.Add(sobj);
                    }
                }
                return results.ToArray();
            }

            public void GetObjectInLine(int fromX, int fromY, int toX, int toY, int stroke, Action<BSObject[], int[][]> callback)
            {
                AddTask(() =>
                {
                    int[][] dots = GenerateLineDots(fromX, fromY, toX, toY, stroke);
                    callback(GetObjectInDots(dots), dots);
                });
            }
            public void GetObjectInArcRange(int x, int y, int angle, int arc, int radius, Action<BSObject[], int[][]> callback)
            {
                AddTask(() =>
                {
                    var dots = GenerateArcDots(x, y, angle - arc / 2, angle + arc / 2, radius);
                    callback(GetObjectInDots(dots), dots);
                });
            }
            public void GetObjectInRoundRange(int x, int y, int pathSize, int radius, Action<ObservedResult[]> callback)
            {
                AddTask(() =>
                {
                    var blockRect = new Rectangle(0, 0, BlockSize, BlockSize);
                    var blocks = LocToBlocks(x, y, pathSize);
                    var blockDic = new Dictionary<string, int[]>();
                    foreach (var b in blocks)
                    {
                        int bx = b[0], by = b[1], ibx = b[2], iby = b[3];
                        var rt = new Rectangle(ibx - radius, iby - radius, radius * 2 + 1, radius * 2 + 1);
                        var rt2 = new Rectangle(ibx - radius + x * pathSize, iby - radius + y * pathSize, radius * 2 + 1, radius * 2 + 1);
                        var key = bx + "," + by;
                        if (blockRect.Contains(rt))
                        {
                            #region int curr block
                            if (!blockDic.ContainsKey(key) && CheckBlock(bx, by))
                            {
                                blockDic.Add(key, new int[] { bx, by });
                            }
                            #endregion
                        }
                        else
                        {
                            #region 计算关联blocks
                            var bradius = (int)Math.Ceiling(radius / (double)BlockSize);
                            int bxx = bx - bradius,
                                byy = by - bradius,
                                bcount = bradius * 2 + 1;

                            for (int x1 = 0; x1 < bcount; x1++)
                            {
                                for (int y1 = 0; y1 < bcount; y1++)
                                {
                                    var bxxx = x1 + bxx;
                                    var byyy = y1 + byy;
                                    var key2 = bxxx + "," + byyy;
                                    var brect = new Rectangle(bxxx * BlockSize, byyy * BlockSize, BlockSize, BlockSize);
                                    if (brect.IntersectsWith(rt2))
                                    {
                                        if (!blockDic.ContainsKey(key2) && CheckBlock(bxxx, byyy))
                                        {
                                            blockDic.Add(key2, new int[] { bxxx, byyy });
                                        }
                                    }
                                }
                            }
                            #endregion
                        }
                    }

                    var results = new List<ObservedResult>();
                    foreach (var b in blockDic.Values)
                    {
                        var objs = GetBlock(b[0], b[1]).GetAllObject();
                        foreach (var objectId in objs)
                        {
                            var sobj = _space.GetBSObject(objectId);
                            var d = CheckDistance(x, y, pathSize, sobj.LocX, sobj.LocY, sobj.PathSize, radius);
                            if (d != -1)
                            {
                                results.Add(new ObservedResult { BSObject = sobj, Distance = d });
                            }
                        }
                    }
                    results.SortByDistance();
                    callback(results.ToArray());
                });
            }

            public bool IsObserver(string objectId)
            {
                return _observerDic.ContainsKey(objectId);
            }
            public void AddObserver(BSObject sobj)
            {
                lock (_observerDic)
                {
                    if (!_observerDic.ContainsKey(sobj.ObjectId))
                    {
                        _observerDic.Add(sobj.ObjectId, new Observer(this, sobj));
                    }
                }
            }
            public void RemoveObserver(string objectId)
            {
                lock (_observerDic)
                {
                    if (_observerDic.ContainsKey(objectId))
                    {
                        _observerDic.Remove(objectId);
                    }
                }
            }
            public Observer GetObserver(string objectId)
            {
                Observer observer = null;
                _observerDic.TryGetValue(objectId, out observer);
                return observer;
            }

            public void ClearObject(BSObject sobj)
            {
                lock (_observerDic)
                {
                    if (_observerDic.ContainsKey(sobj.ObjectId))
                    {
                        _observerDic.Remove(sobj.ObjectId);
                    }
                }

                RemoveObjectInPath(sobj.ObjectId, sobj.LocX, sobj.LocY, sobj.PathSize);

                var blocks = LocToBlocks(sobj.LocX, sobj.LocY, sobj.PathSize);
                foreach (var b in blocks)
                {
                    BlockRemoveObject(b[0], b[1], sobj.ObjectId);
                }
            }
            public void ClearAllObject()
            {
                ClearAllBlockObject();
                ClearAllPathObject();

                lock (_observerDic)
                {
                    _observerDic.Clear();
                }
            }
            public void Set(string objectId, int pathSize, int fromX, int fromY, int toX, int toY)
            {
                AddTask(() =>
                {
                    var fromBlocks = LocToBlocks(fromX, fromY, pathSize);
                    var toBlocks = LocToBlocks(toX, toY, pathSize);
                    foreach (var b in fromBlocks)
                    {
                        BlockRemoveObject(b[0], b[1], objectId);
                    }
                    foreach (var b in toBlocks)
                    {
                        BlockAddObject(b[0], b[1], objectId);
                    }

                    //Console.WriteLine(comm.JsonSerializer.Serialize(fromBlocks));
                    //Console.WriteLine(comm.JsonSerializer.Serialize(toBlocks));

                    lock (_observerDic)
                    {
                        foreach (var observer in _observerDic.Values)
                        {
                            observer.Observed(objectId, toX, toY, pathSize);
                        }
                    }
                });

                _space.OnSObjectChangeLoc(objectId, pathSize, fromX, fromY, toX, toY);
            }
            public bool TrySetPath(string objectId, int pathSize, int fromX, int fromY, int toX, int toY)
            {
                lock (_pathDataDic)
                {
                    if (GetPathType(toX, toY, pathSize).CanEnter())
                    {
                        RemoveObjectInPath(objectId, fromX, fromY, pathSize);
                        AddObjectInPath(objectId, toX, toY, pathSize);
                        return true;
                    }
                    return false;
                }
            }
        }
        #endregion

        public string MapId { get; private set; }
        public string StarId { get; private set; }
        public string SpaceId { get; private set; }
        public int MapWidth { get; private set; }
        public int MapHeight { get; private set; }
        public int MapPathSize { get; private set; }
        public int AllSObjectCount { get { return _sObjList.Count; } }
        public LocManager LocMgr { get; private set; }

        bool _pathMultiObj = true;

        List<BSObject> _sObjList = new List<BSObject>();
        Dictionary<string, BSObject> _sObjDic = new Dictionary<string, BSObject>();

        public BSpace(comm.DefineFile defineFile, string mapId, string starId, string spaceId, bool pathMultiObj, bool locManagerSelfThread, Dictionary<string, objst.DMap.Data.Obj> exObjDic = null)
        {
            var dMapData = defineFile.GetMapData(mapId);
            if (exObjDic != null)
            {
                dMapData.AddExObjDic(exObjDic);
            }
            dMapData.FillAllObjPath(defineFile);

            MapId = mapId;
            StarId = starId;
            SpaceId = spaceId;
            MapWidth = dMapData.Width;
            MapHeight = dMapData.Height;
            MapPathSize = dMapData.PathSize;
            _pathMultiObj = pathMultiObj;

            LocMgr = new BSpace.LocManager(this, dMapData, locManagerSelfThread, 10);
        }

        public abstract string FindPath(int pathSize, int fromX, int fromY, int toX, int toY, Action<FindTaskMode, comm.AStarNode[]> callback);
        public abstract void CancelFindPath(string id);

        protected virtual void OnSObjectChangeLoc(string objectId, int pathSize, int sx, int sy, int ex, int ey)
        {
        }

        protected bool AddBSObject(BSObject sObject)
        {
            lock (_sObjDic)
            {
                if (_sObjDic.ContainsKey(sObject.ObjectId))
                {
                    return false;
                }
                _sObjList.Add(sObject);
                _sObjDic.Add(sObject.ObjectId, sObject);
            }
            return true;
        }
        protected bool RemoveBSObject(string objectId)
        {
            lock (_sObjDic)
            {
                if (_sObjDic.ContainsKey(objectId))
                {
                    var sobj = _sObjDic[objectId];
                    _sObjList.Remove(sobj);
                    _sObjDic.Remove(objectId);

                    LocMgr.ClearObject(sobj);
                    return true;
                }
            }
            return false;
        }
        protected void ClearAllBSObject()
        {
            lock (_sObjDic)
            {
                _sObjDic.Clear();
                _sObjList.Clear();
            }

            LocMgr.ClearAllObject();
        }

        public bool BSObjectExists(string objectId)
        {
            return _sObjDic.ContainsKey(objectId);
        }
        public BSObject[] GetAllBSObject()
        {
            lock (_sObjDic)
            {
                return _sObjList.ToArray();
            }
        }
        public BSObject GetBSObject(string objectId)
        {
            BSObject sobj;
            _sObjDic.TryGetValue(objectId, out sobj);
            return sobj;
        }
    }
}
